package com.ideaaedi.commonspring.spi;

import com.ideaaedi.commonspring.lite.antidupli.AntiDuplication;
import com.ideaaedi.commonspring.parser.SpelUtil;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 防重执行器
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2100.7.3
 */
public abstract class AbstractAntiDuplicateExecutor implements AntiDuplicateExecutor<String> {
    
    @Override
    public int adviceOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 1;
    }
    
    @Override
    public String exec(@NonNull MethodInvocation invocation) {
        Method method = invocation.getMethod();
        Objects.requireNonNull(method, "method should not be null.");
        
        Object[] arguments = invocation.getArguments();
        Objects.requireNonNull(arguments, "arguments should not be null.");
        
        Annotation[] annotations = method.getAnnotations();
        AntiDuplication antiDuplication = AnnotationUtils.getAnnotation(method, AntiDuplication.class);
        Objects.requireNonNull(antiDuplication, "antiDuplication should not be null.");
        // 实际注解类型。要么为AntiDuplication注解，要么为AntiDuplication的子注解
        Annotation actualAntiDuplication = Arrays.stream(annotations).filter((Annotation annotation) -> {
                    if (annotation.annotationType() == AntiDuplication.class) {
                        return true;
                    }
                    return annotation.annotationType().isAnnotationPresent(AntiDuplication.class);
                }
        ).findFirst().orElseThrow(() -> new IllegalStateException("Obtain actualAntiDuplication fail."));
        
        Map<String, Object> antiDuplicationAttributes = AnnotationUtils.getAnnotationAttributes(actualAntiDuplication);
        if (actualAntiDuplication != antiDuplication) {
            Map<String, Object> annotationAttributes = AnnotationUtils.getAnnotationAttributes(antiDuplication);
            annotationAttributes.forEach(antiDuplicationAttributes::putIfAbsent);
        }
        String spel = (String)antiDuplicationAttributes.get("spel");
        long during = (long)antiDuplicationAttributes.get("during");
        TimeUnit unit = (TimeUnit)antiDuplicationAttributes.get("unit");
        String message = (String)antiDuplicationAttributes.get("message");
        String spelVal;
        if (StringUtils.isNotBlank(spel) && spel.contains("#")) {
            Object spelValObj = SpelUtil.parseSpel(method, arguments, Object.class, spel);
            spelVal = spelValObj == null ? null : spelValObj.toString();
        } else {
            spelVal = spel;
        }
        long duringSeconds = unit.toSeconds(during);
        return doExec(spelVal, duringSeconds, message);
    }
    
    @Override
    public void doFinally(Throwable throwable, String execResult, Object targetPointResult) {
    }
    
    /**
     * 防重逻辑
     *
     * @param spelVal 解析后的spel值
     * @param duringSeconds 防重注解中的时长（单位秒）
     * @param message 防重注解上的message<br />注：可作为扩展字段进行使用
     *
     * @return 防重唯一标识
     */
    public abstract String doExec(@Nullable String spelVal, long duringSeconds, @NonNull String message);
}
